﻿/****************************************************************
 * Скрипт предназначен для трансформации XML на стороне клиента
 * (браузера) при помощи XSL
 * В качестве источника может быть использован файл где-то на сервере,
 * доступный по URL, XML-строка или существующий DOM Document
 * 
 * Основной класс classXslTransformer реализует методы
 *     transformToText() – выполнить трансформацию в текст
 *     transformToDocument() – выполнить трансформацию в отдельный новый DOM-документ
 *     transformToFragment(doc) – выполнить трансформацию во фрагмент DOM-документа, 
 *         указанного в аргументе doc
 * Конструктор класса принимает ассоциативный массив параметров, описание которого
 * можно увидеть ниже.
 * 
 *
 *
 * Класс, который производит трансформацию XML документа/строки при помощи XSL
 * 
 * Методы класса
 * transformToDocument - производит трансформацию в отдельный DOM Document
 * transformToText - производит трансформацию в XML строку
 * transformToFragment(doc) - производит трансформацию во фрагмент DOM документа
 * xmlDocumentToString(doc) - сериализует документ в XML строку
 *
 * Исключения
 * все исключенния от класса classTransformationDocument передаются вверх по иерархии "как есть".
 * Дополнительно поднимается исключение
 * 110 - Не удалось добавить параметр к XSL трансформации
 * 111 - Не удалось сериализовать документ как XML строку
 ****************************************************************/

 // Конструктор
// Конструктор принимает три именованных параметра - источник XML, источник XSL и список параметров 
// для трансформации. Для источника возможны следующие значения
// {String} xmlurl - ссылка, по которой необходимо "скачать" XML для трансформации
// {DOMDocument} xmldocument - объект, содержащий XML для трансформации
// {String} xmlstring - строка, содержащая XML для трансформации. XML должен быть well-formed
// {String} xslurl - ссылка, по которой необходимо "скачать" XSL трансформацию
// {DOMDocument} xsldocument - объект, содержащий XSL трансформацию
// {String} xslstring - строка, содержащая XSL данные для трансформации
// 
// Обрабатывается только пара параметров - один для XML и один для XSL группы. Для каждой группы
// используется первый встреченный параметр, остальные - игнорируются. Так, если передаются
// параметры "xmlurl" и "xmlstring", будет использоваться "xmlurl", а "xmlstring" будет проигнорирован
// 
// {Array} params
// Список параметров для трансформации представляет собой ассоциативный массив с парами
// - имя параметра: значение
// 
// Пример использования:
// var tranformer = 
//   new classXslTransformer({
//     'xmlstring': '<catalog><item>...</item></catalog>', 
//     'xslurl': 'http: *www.example.com/data/example_catalog.xsl',
//     'params': {'moneyCode': 'руб', 'expDate': '2012-02-03'}
//   });
 function classXslTransformer(params) {
    if (params['xmlurl']) {
        this.xmlDocument = new transformationDocument();
        this.xmlDocument.url = params['xmlurl'];
    } else if (params['xmldocument']) {
        this.xmlDocument = new transformationDocument();
        this.xmlDocument.xmlDoc = params['xmldocument'];
    } else if (params['xmlstring']) {
        this.xmlDocument = new transformationDocument();
        this.xmlDocument.xmlString = params['xmlstring'];
    }
    if (params['xslurl']) {
        this.xslDocument = new transformationDocument();
        this.xslDocument.url = params['xslurl'];
    } else if (params['xsldocument']) {
        this.xslDocument = new transformationDocument();
        this.xslDocument.xmlDoc = params['xsldocument'];
    } else if (params['xslstring']) {
        this.xslDocument = new transformationDocument();
        this.xslDocument.xmlString = params['xslstring'];
    }

    if (params['params']) {
        this.xslDocument.xslParams = params['params'];
    }
}

// метод осуществляет трансформацию XML/XSL, однако возвращаемый результат
// может быть DOM Document либо же строка, в зависимости от браузера
// оконачательную конвертацию к нужному типу будет производить конкретный
// метод "transformTo..."
// исключения:
// 110 - не удалось добавить параметр к XSL трансформации
classXslTransformer.prototype.transformXML = function () {
    this.xmlDocument.loadXml();
    this.xslDocument.loadXml();

	this.xslDocument.fixIncludes();

    if (window.ActiveXObject) { // IE
        this.xslDocument.addParams();
        return this.xmlDocument.xmlDoc.transformNode(this.xslDocument.xmlDoc);
    } else if (window.XSLTProcessor) { // Mozilla, Firefox, Opera, Safari etc.
        var xsltProcessor = new XSLTProcessor();
        xsltProcessor.importStylesheet(this.xslDocument.xmlDoc);
		// для этих браузеров можно использовать нативный способ установки xsl:param
        for (var p in this.xslDocument.xslParams) {
            try {
                xsltProcessor.setParameter(null, p, this.xslDocument.xslParams[p]);
            } catch (ex) {
                throw {number: 110, message: "Не удалось добавить параметр к XSL трансформации"}
            }
        }
        this.xslDocument.addParams();
        // somehow, transformToFragment works incorrectly, recognizing the
        // result of transformation as xml, not html, because 
        // xsl:output="xhtml" is still not supported, and for xhtml
        // xsl:output="xml" is used 
        // (xsl:output="html" strips namespaces)
        // see: http://osdir.com/ml/mozilla.devel.layout.xslt/2003-10/msg00008.html
        // also, see: https://developer.mozilla.org/en/Using_the_Mozilla_JavaScript_interface_to_XSL_Transformations

        var doc = document.implementation.createDocument("", "", null);
        var resultDocument = xsltProcessor.transformToDocument(this.xmlDocument.xmlDoc, doc);
        return resultDocument;
    }
}


// метод производит трансформацию XML
// возвращает: {DOMDocument} - XML Document с результатом трансформации
classXslTransformer.prototype.transformToDocument = function () {
	var docResult = this.transformXML();
	if(typeof(docResult) == 'string') {
		// полученный результат - строка. Требуется перевести его в XML
		// используем метод нашего transformationDocument
		var tempDoc = new transformationDocument();
		tempDoc.loadFromString(docResult);
		return tempDoc.xmlDoc;
	} else {
		// вернулся объект - это то, что нам надо
		return docResult;
	}
}
// метод производит трансформацию XML
// возвращает: {String} - XML строку с результатом трансформации
classXslTransformer.prototype.transformToString = function () {
	var resultDoc = this.transformXML();
	// в принципе результат уже может быть строкой, но в методе xmlDocumentToString
	// мы дополнительно проверяем нв этот тип, так что повторного приведения строки
	// к строке наверняка не произойдёт
	return this.xmlDocumentToString(resultDoc);
}
// метод производит трансформацию XML
// возвращает: {DocumentFragment} - результат трансформации привязывается
// к переданному документу как дочерний фрагмент
classXslTransformer.prototype.transformToFragment = function (doc) {
    var resultXml = this.transformToString();
    var element = doc.createDocumentFragment();
    element.innerHtml = resultXml;
    return element;
}
// метод конвертирует Xml документ в строку. Использует сериалайзер
// возвращает: {String} - XML строку, представляющую Xml документ
// комментарий: в связи со специфичностью трансформации в Internet Explorer 
// (возвращается строка), проводится дополнительная проверка на тип входящего
// параметра
// исключения:
// 111 - Не удалось сериализовать документ как XML строку
classXslTransformer.prototype.xmlDocumentToString = function(doc) {
	if(typeof(doc) == 'string') {
		// если, по какой-то причине, у нас уже строка - не стоит её больше изменять
		return doc;
	}
	else {
		// иначе пытаемся использовать сериализатор
		try {
			var xmls = new XMLSerializer();
			return xmls.serializeToString(doc);
		} catch(ex) {
			throw {number: 111, message: "Не удалось сериализовать документ как XML строку", original: ex}
		}
	}
}

